package ml.stayfool;

import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.lang.GeoLocation;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifDirectoryBase;
import com.drew.metadata.exif.GpsDirectory;
import ml.stayfool.address.AddressInfo;
import ml.stayfool.configuration.ConfigProps;
import ml.stayfool.util.GPSUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Optional;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Created by stayfool on 2017/1/11.
 */
public class ListFileVisitor extends SimpleFileVisitor<Path> {

    private SimpleDateFormat dfFile = new SimpleDateFormat("yyyy-MM-dd-hh-mm-ss");
    private SimpleDateFormat dfDir = new SimpleDateFormat("yyyy-MM");

    private Logger logger = LoggerFactory.getLogger(getClass());

    private final ConcurrentMap<String, ConcurrentSkipListSet<Path>> fileMap;
    private final ConcurrentSkipListSet<Path> failureFileSet;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();

    public ListFileVisitor(ConcurrentMap<String, ConcurrentSkipListSet<Path>> fileMap,
                           ConcurrentSkipListSet<Path> failureFileSet) {
        this.fileMap = fileMap;
        this.failureFileSet = failureFileSet;
    }

    @Override
    public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {

        String name = path.toString();
        String separator = path.getFileSystem().getSeparator();
        if (!name.contains("."))
            return FileVisitResult.CONTINUE;

        AtomicReference<Date> creDate = new AtomicReference<>();
        AtomicReference<AddressInfo> address = new AtomicReference<>();

        readMetadata(path, creDate, address);

        if (creDate.get() == null) {
            logger.error("can not read create time from file {}, do not move.", path);
            failureFileSet.add(path);
            return FileVisitResult.CONTINUE;
        }

        String prefix = ConfigProps.outdir();
        String suffix = name.substring(name.lastIndexOf("."));

        String newName = prefix + separator;

        if (address.get() != null && address.get().getStatus() == 0) {
            AddressInfo.ResultBean.AddressComponentBean component = address.get().getResult().getAddressComponent();
            String province = component.getProvince();
            String city = component.getCity();
            if (province.equals(city))
                newName += province + separator;
            else
                newName += province + "-" + city + separator;
        }

        newName += dfDir.format(creDate.get()) + separator;

        newName += dfFile.format(creDate.get()) + suffix;

        lock.readLock().lock();
        if (fileMap.containsKey(newName)) {
            fileMap.get(newName).add(path);
            lock.readLock().unlock();
        } else {
            lock.readLock().unlock();
            lock.writeLock().lock();
            if (fileMap.containsKey(newName)) {
                fileMap.get(newName).add(path);
            } else {
                fileMap.put(newName, new ConcurrentSkipListSet<>(Collections.singleton(path)));
            }
            lock.writeLock().unlock();
        }

        logger.info("scan file {}", path);
        return FileVisitResult.CONTINUE;
    }

    // 获取照片拍摄日期和GPS
    private void readMetadata(Path path, AtomicReference<Date> creDate, AtomicReference<AddressInfo> address) {
        Date date = null;
        Metadata m = null;
        try {
            m = ImageMetadataReader.readMetadata(Files.newInputStream(path));
        } catch (ImageProcessingException | IOException e) {
            failureFileSet.add(path);
            logger.error("read exif metadata failed, file : {}, reason : {}", path, e);
        }

        if (m != null) {

            if (m.containsDirectoryOfType(ExifDirectoryBase.class)) {
                ExifDirectoryBase d = m.getFirstDirectoryOfType(ExifDirectoryBase.class);

                date = d.getDate(ExifDirectoryBase.TAG_DATETIME_ORIGINAL);
                if (date == null)
                    date = d.getDate(ExifDirectoryBase.TAG_DATETIME_DIGITIZED);
                if (date == null)
                    date = d.getDate(ExifDirectoryBase.TAG_DATETIME);
            }

            if (m.containsDirectoryOfType(GpsDirectory.class)) {
                GpsDirectory d = m.getFirstDirectoryOfType(GpsDirectory.class);
                GeoLocation geo = d.getGeoLocation();

                if (geo != null) {
                    String location = geo.getLatitude() + "," + geo.getLongitude();
                    Optional<AddressInfo> optional = GPSUtil.getAddress(location);
                    address.set(optional.isPresent() ? optional.get() : null);
                }
            }
        }

        try {
            if (date == null)
                date = new Date(Files.getLastModifiedTime(path).toMillis());
        } catch (IOException e) {
            logger.error("read file attribute failed,", e);
        }

        creDate.set(date);
    }

}
